Struct System.Span<T>

Artikel ini menyediakan keterangan tambahan untuk dokumentasi referensi untuk API ini.

Jenisnya Span<T> adalah struct ref yang dialokasikan pada tumpukan daripada pada tumpukan terkelola. Jenis struct Ref memiliki sejumlah batasan untuk memastikan bahwa mereka tidak dapat dipromosikan ke tumpukan terkelola, termasuk bahwa mereka tidak dapat dikotak, mereka tidak dapat ditetapkan ke variabel jenis Object, dynamic atau ke jenis antarmuka apa pun, mereka tidak dapat menjadi bidang dalam jenis referensi, dan tidak dapat digunakan di seluruh await dan yield batas. Selain itu, panggilan ke dua metode, Equals(Object) dan GetHashCode, melemparkan NotSupportedException.

Penting

Karena ini adalah jenis tumpukan-saja, Span<T> tidak cocok untuk banyak skenario yang memerlukan penyimpanan referensi ke buffer pada tumpukan. Ini benar, misalnya, rutinitas yang melakukan panggilan metode asinkron. Untuk skenario tersebut, Anda dapat menggunakan pelengkap System.Memory<T> dan System.ReadOnlyMemory<T> jenis.

Untuk rentang yang mewakili struktur yang tidak dapat diubah atau baca-saja, gunakan System.ReadOnlySpan<T>.

Memori

mewakili Span<T> wilayah memori arbitrer yang berdekatan. Instans Span<T> sering digunakan untuk menahan elemen array atau sebagian array. Namun, tidak seperti array, Span<T> instans dapat menunjuk ke memori terkelola, memori asli, atau memori yang dikelola pada tumpukan. Contoh berikut membuat Span<Byte> dari array:

// Create a span over an array.
var array = new byte[100];
var arraySpan = new Span<byte>(array);

byte data = 0;
for (int ctr = 0; ctr < arraySpan.Length; ctr++)
    arraySpan[ctr] = data++;

int arraySum = 0;
foreach (var value in array)
    arraySum += value;

Console.WriteLine($"The sum is {arraySum}");
// Output:  The sum is 4950
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array

let mutable data = 0uy
for i = 0 to arraySpan.Length - 1 do
    arraySpan[i] <- data
    data <- data + 1uy

let mutable arraySum = 0
for value in array do
    arraySum <- arraySum + int value

printfn $"The sum is {arraySum}"
// Output:  The sum is 4950

Contoh berikut membuat Span<Byte> dari 100 byte memori asli:

// Create a span from native memory.
var native = Marshal.AllocHGlobal(100);
Span<byte> nativeSpan;
unsafe
{
    nativeSpan = new Span<byte>(native.ToPointer(), 100);
}
byte data = 0;
for (int ctr = 0; ctr < nativeSpan.Length; ctr++)
    nativeSpan[ctr] = data++;

int nativeSum = 0;
foreach (var value in nativeSpan)
    nativeSum += value;

Console.WriteLine($"The sum is {nativeSum}");
Marshal.FreeHGlobal(native);
// Output:  The sum is 4950
// Create a span from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)

let mutable data = 0uy
for i = 0 to nativeSpan.Length - 1 do
    nativeSpan[i] <- data
    data <- data + 1uy

let mutable nativeSum = 0
for value in nativeSpan do
    nativeSum <- nativeSum + int value

printfn $"The sum is {nativeSum}"
Marshal.FreeHGlobal native
// Output:  The sum is 4950

Contoh berikut menggunakan kata kunci stackalloc C# untuk mengalokasikan 100 byte memori pada tumpukan:

// Create a span on the stack.
byte data = 0;
Span<byte> stackSpan = stackalloc byte[100];
for (int ctr = 0; ctr < stackSpan.Length; ctr++)
    stackSpan[ctr] = data++;

int stackSum = 0;
foreach (var value in stackSpan)
    stackSum += value;

Console.WriteLine($"The sum is {stackSum}");
// Output:  The sum is 4950
    // Create a span on the stack.
    let mutable data = 0uy
    let stackSpan = 
        let p = NativeInterop.NativePtr.stackalloc<byte> 100 |> NativeInterop.NativePtr.toVoidPtr
        Span<byte>(p, 100)

    for i = 0 to stackSpan.Length - 1 do
        stackSpan[i] <- data
        data <- data + 1uy

    let mutable stackSum = 0
    for value in stackSpan do
        stackSum <- stackSum + int value

    printfn $"The sum is {stackSum}"
// Output:  The sum is 4950

Karena Span<T> merupakan abstraksi atas blok memori semena-mena, metode jenis Span<T> dan metode dengan Span<T> parameter beroperasi pada objek apa pun Span<T> terlepas dari jenis memori yang dienkapsulasinya. Misalnya, setiap bagian kode terpisah yang menginisialisasi rentang dan menghitung jumlah elemennya dapat diubah menjadi metode inisialisasi dan perhitungan tunggal, seperti yang diilustrasikan contoh berikut:

public static void WorkWithSpans()
{
    // Create a span over an array.
    var array = new byte[100];
    var arraySpan = new Span<byte>(array);

    InitializeSpan(arraySpan);
    Console.WriteLine($"The sum is {ComputeSum(arraySpan):N0}");

    // Create an array from native memory.
    var native = Marshal.AllocHGlobal(100);
    Span<byte> nativeSpan;
    unsafe
    {
        nativeSpan = new Span<byte>(native.ToPointer(), 100);
    }

    InitializeSpan(nativeSpan);
    Console.WriteLine($"The sum is {ComputeSum(nativeSpan):N0}");

    Marshal.FreeHGlobal(native);

    // Create a span on the stack.
    Span<byte> stackSpan = stackalloc byte[100];

    InitializeSpan(stackSpan);
    Console.WriteLine($"The sum is {ComputeSum(stackSpan):N0}");
}

public static void InitializeSpan(Span<byte> span)
{
    byte value = 0;
    for (int ctr = 0; ctr < span.Length; ctr++)
        span[ctr] = value++;
}

public static int ComputeSum(Span<byte> span)
{
    int sum = 0;
    foreach (var value in span)
        sum += value;

    return sum;
}
// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950
open System
open System.Runtime.InteropServices
open FSharp.NativeInterop

// Package FSharp.NativeInterop.NativePtr.stackalloc for reuse.
let inline stackalloc<'a when 'a: unmanaged> length : Span<'a> =
    let voidPointer = NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr
    Span<'a>(voidPointer, length)

let initializeSpan (span: Span<byte>) =
    let mutable value = 0uy
    for i = 0 to span.Length - 1 do
        span[i] <- value
        value <- value + 1uy

let computeSum (span: Span<byte>) =
    let mutable sum = 0
    for value in span do
        sum <- sum + int value
    sum

let workWithSpans () =
    // Create a span over an array.
    let array = Array.zeroCreate<byte> 100
    let arraySpan = Span<byte> array

    initializeSpan arraySpan
    printfn $"The sum is {computeSum arraySpan:N0}"

    // Create an array from native memory.
    let native = Marshal.AllocHGlobal 100
    let nativeSpan = Span<byte>(native.ToPointer(), 100)

    initializeSpan nativeSpan
    printfn $"The sum is {computeSum nativeSpan:N0}"

    Marshal.FreeHGlobal native

    // Create a span on the stack.
    let stackSpan = stackalloc 100

    initializeSpan stackSpan
    printfn $"The sum is {computeSum stackSpan:N0}"

// The example displays the following output:
//    The sum is 4,950
//    The sum is 4,950
//    The sum is 4,950

Larik

Ketika membungkus array, Span<T> dapat membungkus seluruh array, seperti yang terjadi pada contoh di bagian Memori . Karena mendukung pemotongan, Span<T> juga dapat menunjuk ke rentang yang bersebelahan dalam array.

Contoh berikut membuat iringan lima elemen tengah dari array bilangan bulat 10 elemen. Perhatikan bahwa kode menggandakan nilai setiap bilangan bulat dalam ikatan. Seperti yang ditunjukkan oleh output, perubahan yang dibuat oleh rentang tercermin dalam nilai array.

using System;

var array = new int[] { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
var slice = new Span<int>(array, 2, 5);
for (int ctr = 0; ctr < slice.Length; ctr++)
    slice[ctr] *= 2;

// Examine the original array values.
foreach (var value in array)
    Console.Write($"{value}  ");
Console.WriteLine();

// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20
module Program

open System

[<EntryPoint>]
let main _ =
    let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
    let slice = Span<int>(array, 2, 5)
    for i = 0 to slice.Length - 1 do
        slice[i] <- slice[i] * 2

    // Examine the original array values.
    for value in array do
        printf $"{value}  "
    printfn ""
    0
// The example displays the following output:
//      2  4  12  16  20  24  28  16  18  20

Irisan

Span<T> termasuk dua kelebihan beban Slice metode yang membentuk ikatan dari rentang saat ini yang dimulai pada indeks tertentu. Hal ini memungkinkan untuk memperlakukan data dalam Span<T> sekumpulan gugus logis yang dapat diproses sesuai kebutuhan oleh bagian alur pemrosesan data dengan dampak performa minimal. Misalnya, karena protokol server modern sering kali berbasis teks, manipulasi string dan substring sangat penting. String Di kelas , metode utama untuk mengekstrak substring adalah Substring. Untuk alur data yang mengandalkan manipulasi string yang luas, penggunaannya menawarkan beberapa penalti performa, karena:

  1. Membuat string baru untuk menahan substring.
  2. Menyalin subset karakter dari string asli ke string baru.

Operasi alokasi dan penyalinan ini dapat dihilangkan dengan menggunakan Span<T> atau ReadOnlySpan<T>, seperti yang ditunjukkan contoh berikut:

using System;

class Program2
{
    static void Run()
    {
        string contentLength = "Content-Length: 132";
        var length = GetContentLength(contentLength.ToCharArray());
        Console.WriteLine($"Content length: {length}");
    }

    private static int GetContentLength(ReadOnlySpan<char> span)
    {
        var slice = span.Slice(16);
        return int.Parse(slice);
    }
}
// Output:
//      Content length: 132
module Program2

open System

let getContentLength (span: ReadOnlySpan<char>) =
    let slice = span.Slice 16
    Int32.Parse slice

let contentLength = "Content-Length: 132"
let length = getContentLength (contentLength.ToCharArray())
printfn $"Content length: {length}"
// Output:
//      Content length: 132